微前端应用(qiankun+umi+antd) 您所在的位置:网站首页 antd table loading 微前端应用(qiankun+umi+antd)

微前端应用(qiankun+umi+antd)

#微前端应用(qiankun+umi+antd)| 来源: 网络整理| 查看: 265

1.微前端介绍以应用选型 1.1什么是微前端?

微前端是一种前端架构模式,它将前端应用程序拆分成多个小型的、独立开发、独立部署的子应用,然后将这些子应用组合成一个大型的、复杂的前端应用。每个子应用都有自己的技术栈、独立的代码库、独立的开发、测试和部署流程,并且可以独立运行、测试和部署。

微前端的目的是解决大型前端应用程序在开发、测试和部署等方面的复杂性和困难。通过将前端应用程序拆分成多个独立的子应用,可以实现团队的分工协作、提高开发效率、降低代码耦合性、提高代码可维护性和可测试性。同时,微前端也可以实现应用程序的增量更新、灰度发布、动态加载和懒加载等功能,从而提高应用程序的性能和用户体验。

1.2技术选择

主应用:umi(Ant Design Pro)

子应用:umi(Ant Design Pro)

父应用和子应用其实都是独立的前端项目,父应用可以在内部引入子应用,子应用也可以在自己内部继续引入孙子应用

2.开始使用 2.1配置父应用

首先需要配置父应用,注册子应用的相关信息,这样父应用才能识别子应用并在内部引入。

注册子应用的方式主要有两种:

插件注册子应用。 运行时注册子应用。

以上两种方式都是在 .umirc.ts 中注册子应用,但是使用的插件不同,从而导致了使用方式的不同。

插件注册子应用:

使用插件注册子应用时,需要安装 @umijs/plugin-qiankun 插件,并在 .umirc.ts 中进行配置,如下:

import { defineConfig } from 'umi'; export default defineConfig({ qiankun: { slave: {}, master: { apps: [ { name: 'reactApp', // 唯一 id entry: 'http://localhost:8091', // html entry }, ], // sandbox: false, }, }, }); 复制代码

在 qiankun 配置中,slave 为子应用的配置,可以在这里配置子应用的名称、入口、路由、公共依赖等信息。该插件会自动构建子应用的入口文件、修改 webpack 配置等。

注意:没有.umirc.ts文件就写在config.ts中,上述中如果不添加slave: {}配置信息,会报错:Unhandled Rejection (Error): register failed, invalid key useQiankunStateForSlave from plugin ../../app.tsx.如果有这个报错请参考这种方法

运行时注册子应用:

运行时注册子应用时,不需要安装额外的插件,只需要在 .umirc.ts 中进行配置,如下:

import { defineConfig } from 'umi'; export default defineConfig({ // 省略其他配置 routes: [ { path: '/', component: '@/layouts/index', routes: [ { path: '/', component: '@/pages/index', microApp: 'sub-app', // 运行时注册子应用 }, ], }, ], }); 复制代码

在 routes 中配置 microApp 字段即可运行时注册子应用。在这里可以配置子应用的名称、入口、路由等信息。

两种方式的目的都是为了在主应用中注册子应用,只是使用的插件和配置方式有所不同。如果使用 @umijs/plugin-qiankun 插件,则可以自动构建子应用的入口文件、修改 webpack 配置等,使用起来更加方便。如果不使用插件,则需要手动配置子应用的入口、路由等信息。

2.2配置子应用

子应用需要导出必要的生命周期钩子,供父应用在适当的时机调用。

假设您的子应用项目基于 Umi 开发且**引入了 qiankun **插件****。如果没有,可以按照此教程进行配置。

修改子应用的 Umi 的配置文件,添加如下内容:

// .umirc.ts export default { qiankun: { slave: {}, }, }; 复制代码

这样,微前端插件会自动在项目中创建好 Qiankun 子应用所需的生命周期钩子和方法.

本地调试时,主应用中点击子应用的路由地址,报错”Unhandled Rejection (TypeError): Failed to fetch”,

要解决这个问题,有两种方法可以尝试:

配置子应用的跨域规则

在子应用的配置中,通过配置跨域规则来允许主应用访问子应用的资源。具体来说,可以在子应用的配置文件中(例如 umi 项目的 config/config.ts 或 config/config.prod.ts 文件)加入如下配置:

export default { // ... devServer: { // 允许跨域访问的域名,如果有多个可以用逗号隔开 headers: { 'Access-Control-Allow-Origin': '*', }, }, // ... }; 复制代码

这个配置会在子应用启动时,自动为子应用启动一个 devServer 服务,并允许跨域访问。

  2.配置主应用代理

export default { // ... proxy: { '/app1': { target: 'http://localhost:8001', // 子应用1的服务地址 changeOrigin: true, pathRewrite: { '^/app1': '', }, }, '/app2': { target: 'http://localhost:8002', // 子应用2的服务地址 changeOrigin: true, pathRewrite: { '^/app2': '', }, }, // ... }, // ... }; 复制代码

这个配置将会把以 /app1 或 /app2 开头的请求,分别代理到子应用1和子应用2所在的服务上。注意,在使用代理时,要确保子应用的服务已经启动并监听了对应的端口号。

2.3引入子应用

在父应用中引入子应用,插件提供了三种不同实现的方式:

路由绑定引入子应用。  组件引入子应用。  组件引入子应用。 2.3.1路由绑定引入子应用

手动配置 config.ts 文件中的 routes 项,通过路由的方式绑定子应用。何时使用:

子应用包含完整的路由切换逻辑时。 父子应用路由相互关联时。

现在,可以配置父应用下的一个子应用"react子应用",路由如下:

{ name: 'react子应用', path: '/subreact', microApp: 'reactApp', microAppProps: { autoSetLoading: true, }, routes: [ { name: 'Welcome', icon: 'smile', path: '/subreact/Welcome', }, { name: 'table-list', icon: 'table', path: '/subreact/list', // 如果想要将 /subreact/list 下所有子路由都关联给微应用 app1,可以带上 * 通配符 }, ], }, 复制代码

配置好后,子应用的路由 base 会在运行时被设置为主应用中配置的 path。 例如,在上面的配置中,我们指定了 react子应用 关联的 path 为 /subreact/Welcome,假如 app1 里有一个路由配置为 /user,当我们想在父应用中访问 /user 对应的页面时,浏览器的 url 需要是 base + /user,即 /subreact/user 路径,否则子应用会因为无法匹配到正确的路由而渲染空白或404页面。

可以手动修改路由配置信息:

if (window.__POWERED_BY_QIANKUN__) { routesData.map(item => { if (item.path.includes('/')) { item.path = '/subreact' + item.path } if (item.redirect) { item.redirect = '/subreact' + item.redirect } return item }) } 复制代码

2.3.2 组件引入子应用

通过  组件加载(或卸载)子应用。何时使用:

子应用包含完整的路由切换逻辑时。 父子应用路由相互关联时。

现在,我们想在父应用的某个页面中引入子应用 app1,可以编写代码如下:

import { MicroApp } from 'umi'; export default function Page() { return ; }; 复制代码

使用该方式引入子应用时,父子应用的路由将一一对应。例如,当父应用路由为 /some/page 时,子应用路由同样为 /some/page。切换子应用路由时,父应用将同步切换。

如果父应用的路由包含前缀,可以通过配置 base 属性保证父子应用的路由正确对应。例如,父应用路由为 /main-react/welcome 时,我们希望子应用的路由为 /subreact/welcome,可以修改代码如下:

import { MicroApp } from 'umi'; export default function Page() { return }; 复制代码

2.3.3 组件引入子应用

通过  组件加载(或卸载)子应用。何时使用:

仅使用子应用的指定路由时。 父子应用路由相互独立时。

 组件是  组件的变体,您需要显式提供 url 属性作为子应用的路由。当父应用的路由发生变化时,子应用的路由不会改变。

现在,我们想在父应用的某个组件内部引入 subreact 子应用,子应用的路由为 /subreact/welcome,可以编写代码如下:

复制代码

2.4子应用之间跳转

这个我暂时未尝试成功,会报错MicroAppLink 导出错误问题,成功的小伙伴欢迎补充.下面是具体内容:

如果子应用通过路由绑定的方式引入,在其它子应用的内部,可以使用  跳转到对应的路由。以子应用 app1 和 app2 为例:

// 在 app1 中 import { MicroAppLink } from 'umi'; export default function Page() { return ( {/* 跳转链接为 /app2/home */} go to app2 ); } 复制代码

在上面的例子中,点击按钮后,父应用的路由变为 /app2/home,渲染子应用 app2 内部路由为 /home 的页面。同理,如果想要从子应用 app2 回到子应用 app1,可以编写代码如下:

// 在 app2 中 import { MicroAppLink } from 'umi'; export default function Page() { return ( {/* 跳转链接为 /app1/project/home */} go to app1 ); } 复制代码

您也可以从子应用跳转到父应用的指定路由:

// 在子应用中 import { MicroAppLink } from 'umi'; export default function Page() { return ( {/* 跳转链接为 /table */} go to master app ); } 复制代码

补充:可以通过路由引用和组件引用同一个子应用

3.子应用生命周期 3.1父应用配置生命周期钩子

在父应用的 src/app.ts 中导出 qiankun 对象进行全局配置,所有的子应用都将实现这些生命周期钩子:

// src/app.ts export const qiankun = { lifeCycles: { // 所有子应用在挂载完成时,打印 props 信息 async afterMount(props) { console.log(props); }, }, }; 复制代码

3.2子应用配置生命周期钩子

在子应用的 src/app.ts 中导出 qiankun 对象,实现生命周期钩子。子应用运行时仅支持配置 bootstrap、mount 和 unmount 钩子:

/ src/app.ts export const qiankun = { // 应用加载之前 async bootstrap(props) { console.log('app1 bootstrap', props); }, // 应用 render 之前触发 async mount(props) { console.log('app1 mount', props); }, // 应用卸载之后触发 async unmount(props) { console.log('app1 unmount', props); }, }; 复制代码

会将引用子组件时传递给子组件的函数和参数都打印出来,这个下面组件传递信息会讲到,且props中包括子应用的信息,如下:

​编辑

4.父子应用通信

父子应用间的通信有两种实现的方法:

基于 useModel() 的通信。这是 Umi 推荐的解决方案。 基于配置的通信。 4.1基于 useModel() 的通信

该通信方式基于 数据流 插件,此插件已经内置于 @umi/max 解决方案当中。

该通信方式需要子应用基于 Umi 开发且引入了该数据流插件。

关于此插件的详细介绍可见数据流指南。

4.1.1主应用透传数据

如果通过路由的模式引入子应用,则需要在父应用的 src/app.ts 里导出一个名为 useQiankunStateForSlave() 的函数,该函数的返回值将传递给子应用:

// src/app.ts export function useQiankunStateForSlave() { const [globalState, setGlobalState] = useState({ slogan: 'Hello MicroFrontend', }); return { globalState, setGlobalState, }; } 复制代码

如果通过组件的模式引入子应用,直接将数据以组件参数的形式传递给子应用即可:

import React, { useState } from 'react'; import { MicroApp } from 'umi'; export default function Page() { const [globalState, setGlobalState] = useState({ slogan: 'Hello MicroFrontend', }); return ( ); }; 复制代码

4.1.2子应用消费数据

子应用会自动生成一个全局的 Model,其命名空间为 @@qiankunStateFromMaster。通过 useModel() 方法,允许子应用在任意组件中获取并消费父应用透传的数据,如下所示:

import { useModel } from 'umi'; export default function Page() { const masterProps = useModel('@@qiankunStateFromMaster'); return {JSON.stringify(masterProps)}; }; 复制代码

或者可以通过高阶方法 connectMaster() 来获取并消费父应用透传的数据,如下所示:

import { connectMaster } from 'umi'; function MyPage(props) { return {JSON.stringify(props)}; } export default connectMaster(MyPage); 复制代码

子应用也可以在生命周期钩子中能够获取并消费得到的 props 属性,根据需求实现对应的生命周期钩子即可。

特别的,当父应用使用  或  组件的方式引入子应用时,会额外向子应用传递一个 setLoading() 方法,允许子应用在合适的时机执行,标记子应用加载为完成状态:

const masterProps = useModel('@@qiankunStateFromMaster'); masterProps.setLoading(false); // 或者 function MyPage(props) { props.setLoading(false); } connectMaster(MyPage); 复制代码

注:setLoading() 方法可以和自定义加载配套使用,在主应用中通过组件引入子应用时,自定义loader,如下:

//CustomLoader.tsx import { Alert, Spin } from 'antd'; import * as React from 'react'; interface ICustomLoaderProps { loading: boolean } const CustomLoader: React.FC = (props) => { const { loading } = props return ; }; export default CustomLoader; 复制代码

import CustomLoader from '@/components/CustomLoader'; } /> 复制代码

下面例举一下组件引用方式时,子应用通过父应用传递过来的方法修改父应用中的值:

const masterProps = useModel('@@qiankunStateFromMaster'); const onChangeGlobalState = () => { const updatedGlobalState = { ...masterProps?.globalState, slogan: '我通过子应用成功修改父应用中的值', }; masterProps?.setGlobalState(updatedGlobalState); }; 复制代码

注意:

子应用中使用 useModel 获取到的 masterProps 对象包含了从主应用中传递过来的 globalState 和 setGlobalState 方法。然而,直接使用 setGlobalState 修改 globalState 时可能无法实现更新。

这是因为 masterProps 中包含的 globalState 是主应用中的状态,子应用不能直接修改主应用中的状态。子应用只能通过 setGlobalState 方法向主应用传递更新后的状态,并让主应用自己进行更新。因此,在子应用中使用 setGlobalState 方法时,需要将整个更新后的状态对象作为参数传递给 setGlobalState 方法。而不能直接修改 globalState 对象中的某个属性值。

正确的做法是,在子应用中使用 setGlobalState 方法时,先从 masterProps 中获取当前的 globalState 对象,然后将需要更新的属性值进行修改,最后将整个更新后的 globalState 对象传递给 setGlobalState 方法。

或者可以使用下面这种方法:

import { useModel } from 'umi'; import { Button } from 'antd'; function SubApp() { const { globalState, setGlobalState } = useModel('@@qiankunStateFromMaster'); const handleClick = () => { setGlobalState({ ...globalState, slogan: 'New Slogan from Sub App', }); }; return ( {globalState.slogan} 更改父应用globalState ); } export default SubApp; 复制代码

参考文档:

微前端 | UmiJS



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有